home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / RandomDot 1.0.2 / Random Dot Src ƒ / _c / ZoomCode.c < prev   
Encoding:
C/C++ Source or Header  |  1994-10-18  |  11.6 KB  |  308 lines  |  [TEXT/KAHL]

  1. /* ZoomCode.c -
  2.     Note: this code is from develop magazine, issue 17. It is referreed by Apple and
  3.     may be freely used in Macintosh programs.
  4.  */
  5. #include "ZoomCode.h"
  6.  
  7. struct ZoomData {
  8.     GDHandle        screenWithLargestPartOfWindow;
  9.     unsigned long    largestArea;
  10.     Rect            windowBounds;
  11. };
  12. typedef struct ZoomData ZoomData, *ZoomDataPtr;
  13.  
  14.  
  15. enum {
  16.     kNudgeSlop    =    4,
  17.     kIconSpace    =    64
  18. };
  19.  
  20. static pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  21.                                     long userData);
  22.  
  23. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  24.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  25.                             short screenEdge1, short screenEdge2);
  26.  
  27. static RgnHandle GetWindowContentRegion(WindowPeek theWindow);
  28. static RgnHandle GetWindowStructureRegion(WindowPeek theWindow);
  29. static void GetWindowPortRect(WindowPeek theWindow, Rect *portRect);
  30. static void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState);
  31. static void GetWindowUserState(WindowPeek theWindow, Rect *userState);
  32.  
  33.  
  34. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  35.                     CalcIdealDocumentSizeProcPtr calcRoutine)
  36. {
  37.     ZoomData    zoomData;
  38.     Rect        newStandardRect;
  39.     Rect        scratchRect;
  40.     Rect        screenRect;
  41.     Rect        portRect;
  42.     Rect        contentRegionBoundingBox;
  43.     Rect        structureRegionBoundingBox;
  44.     Rect        deviceLoopRect;
  45.     GrafPtr        currentPort;
  46.     RgnHandle    scratchRegion;
  47.     RgnHandle    contentRegion;
  48.     RgnHandle    structureRegion;
  49.     GDHandle    mainDevice;
  50.     short        horizontalAmountOffScreen;
  51.     short        verticalAmountOffScreen;
  52.     short        windowFrameTopSize;
  53.     short        windowFrameLeftSize;
  54.     short        windowFrameRightSize;
  55.     short        windowFrameBottomSize;
  56.     static DeviceLoopDrawingUPP deviceLoopUpdate = nil;
  57.     
  58.  
  59.     GetPort(¤tPort);
  60.     SetPort((WindowPtr) theWindow);
  61.     contentRegion = GetWindowContentRegion(theWindow);
  62.     structureRegion = GetWindowStructureRegion(theWindow);
  63.     GetWindowPortRect(theWindow, &portRect);
  64.     contentRegionBoundingBox = (**contentRegion).rgnBBox;
  65.     structureRegionBoundingBox = (**structureRegion).rgnBBox;
  66.     
  67.     // Determine the size of the window frame
  68.     windowFrameTopSize = contentRegionBoundingBox.top - 
  69.                                     structureRegionBoundingBox.top;
  70.     windowFrameLeftSize = contentRegionBoundingBox.left - 
  71.                                     structureRegionBoundingBox.left;
  72.     windowFrameRightSize = structureRegionBoundingBox.right - 
  73.                                     contentRegionBoundingBox.right;
  74.     windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  75.                                     contentRegionBoundingBox.bottom;
  76.                                     
  77.     // If the window is being zoomed into the standard state, calculate the best size
  78.     // to display the window’s information.
  79.     mainDevice = GetMainDevice();
  80.     if (zoomState == inZoomOut) {
  81.         zoomData.screenWithLargestPartOfWindow = mainDevice;
  82.         zoomData.largestArea = 0;
  83.     
  84.         // Usually, we would use the content region’s bounding box to determine the monitor
  85.         // with largest portion of the window’s area. However, if the entire content region
  86.         // of the window is not on any screen, the structure region should be used instead.
  87.         scratchRegion = NewRgn();
  88.         SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
  89.         if (EmptyRgn(scratchRegion))
  90.             zoomData.windowBounds = structureRegionBoundingBox;
  91.         else
  92.             zoomData.windowBounds = contentRegionBoundingBox;
  93.     
  94.         // Use DeviceLoop to walk through all the active screens to find the one with the
  95.         // largest portion of the zoomed window
  96.         deviceLoopRect = zoomData.windowBounds;
  97.         GlobalToLocal((Point *)&deviceLoopRect);
  98.         GlobalToLocal((Point *)&deviceLoopRect.bottom);
  99.         RectRgn(scratchRegion, &deviceLoopRect);
  100.         if(nil == deviceLoopUpdate){
  101.             deviceLoopUpdate = NewDeviceLoopDrawingProc(CalcWindowAreaOnScreen);
  102.         }
  103.         DeviceLoop(scratchRegion, deviceLoopUpdate, (long) &zoomData,
  104.                     (DeviceLoopFlags) singleDevices);
  105.         DisposeRgn(scratchRegion);
  106.         screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  107.         
  108.         // If the monitor being zoomed to is the main monitor, change the top of the
  109.         // useable screen area to avoid putting the title bar underneath the menubar.
  110.         if (zoomData.screenWithLargestPartOfWindow == mainDevice)
  111.             screenRect.top += GetMBarHeight();
  112.             
  113.         // Go figure out the perfect size for the window as if we had an infinitely large
  114.         // screen
  115.         (*calcRoutine)((WindowPtr) theWindow, &newStandardRect);
  116.         
  117.         // Anchor the new rectangle at the window’s current top left corner
  118.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  119.         OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
  120.                     contentRegionBoundingBox.top);
  121.         
  122.         // newStandardRect is the ideal size for the content area. The window frame
  123.         // needs to be accounted for when we see if the window needs to be moved,
  124.         // or resized, so add in the dimensions of the window frame.
  125.         newStandardRect.top -= windowFrameTopSize;
  126.         newStandardRect.left -= windowFrameLeftSize;
  127.         newStandardRect.right += windowFrameRightSize;
  128.         newStandardRect.bottom += windowFrameBottomSize;
  129.         
  130.         // If the new rectangle falls off the edge of the screen, nudge it so that it’s just
  131.         // on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
  132.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  133.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  134.             horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  135.                                                                newStandardRect.right,
  136.                                                                scratchRect.left,
  137.                                                                scratchRect.right,
  138.                                                                screenRect.left,
  139.                                                                screenRect.right);
  140.             verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  141.                                                             newStandardRect.bottom,
  142.                                                             scratchRect.top,
  143.                                                             scratchRect.bottom,
  144.                                                             screenRect.top,
  145.                                                             screenRect.bottom);
  146.             OffsetRect(&newStandardRect, horizontalAmountOffScreen,
  147.                         verticalAmountOffScreen);
  148.         }
  149.     
  150.         // If we’re still falling off the edge of the screen, that means that the perfect
  151.         // size is larger than the screen, so we need to shrink down the standard size
  152.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  153.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  154.  
  155.         // First shrink the width of the window. If the window is wider than the screen
  156.         // it is zooming to, we can just pin the standard rectangle to the edges of the
  157.         // screen, leaving some slop. If the window is narrower than the screen, we know
  158.         // we just nudged it into position, so nothing needs to be done.
  159.             if ((newStandardRect.right - newStandardRect.left) >
  160.                 (screenRect.right - screenRect.left)) {
  161.                 newStandardRect.left = screenRect.left + kNudgeSlop;
  162.                 newStandardRect.right = screenRect.right - kNudgeSlop;
  163.  
  164.                 if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
  165.                     (newStandardRect.right > (screenRect.right - kIconSpace)))
  166.                     newStandardRect.right = screenRect.right - kIconSpace;
  167.             }
  168.  
  169.             // Move in the top. Like the width of the window, nothing needs to be done unless
  170.             // the window is taller than the height of the screen.
  171.             if ((newStandardRect.bottom - newStandardRect.top) >
  172.                 (screenRect.bottom - screenRect.top)) {
  173.                 newStandardRect.top = screenRect.top + kNudgeSlop;
  174.                 newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
  175.             }
  176.         }
  177.  
  178.         // We’ve got the best possible window position. Remove the
  179.         // frame, slam it into the WStateData record and let ZoomWindow
  180.         // take care of the rest.
  181.         newStandardRect.top += windowFrameTopSize;
  182.         newStandardRect.left += windowFrameLeftSize;
  183.         newStandardRect.right -= windowFrameRightSize;
  184.         newStandardRect.bottom -= windowFrameBottomSize;
  185.         SetWindowStandardState(theWindow, &newStandardRect);
  186.     }
  187.     else
  188.         GetWindowUserState(theWindow, &newStandardRect);
  189.         
  190.     // If the window is still anchored at the current location, then just resize it
  191.     if ((newStandardRect.left == contentRegionBoundingBox.left) &&
  192.         (newStandardRect.top == contentRegionBoundingBox.top)) {
  193.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  194.         SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom,
  195.                     true);
  196.     }
  197.     else {
  198.         scratchRegion = NewRgn();
  199.         GetClip(scratchRegion);
  200.         ClipRect(&portRect);
  201.         EraseRect(&portRect);
  202.         ZoomWindow((WindowPtr) theWindow, zoomState, false);
  203.         SetClip(scratchRegion);
  204.         DisposeRgn(scratchRegion);
  205.     }
  206.     
  207.     SetPort(currentPort);
  208. }
  209.  
  210. static pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  211. {
  212.     ZoomDataPtr    zoomData;
  213.     long        windowAreaOnScreen;
  214.     Rect        windowPortionOnScreen;
  215.  
  216.     zoomData = (ZoomDataPtr) userData;
  217.  
  218.     // Find the rectangle that encloses the intersection of the window and this screen.
  219.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  220.     
  221.     // Offset the rectangle so that it’s right and bottom are also it’s width and height.
  222.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  223.     
  224.     // Calculate the area of the portion of the window that’s on this screen.
  225.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  226.     
  227.     // If this is the largest portion of the window that has been encountered so far,
  228.     // remember this screen as the potential screen to zoom to.
  229.     if (windowAreaOnScreen > zoomData->largestArea) {
  230.         zoomData->largestArea = windowAreaOnScreen;
  231.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  232.     }
  233. }
  234.  
  235. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  236. // the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll
  237. // make it fit later on.
  238.  
  239. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  240.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  241. {
  242.     short    offsetAmount;
  243.  
  244.     // First check to see if the window fits on the screen in this dimension.
  245.     if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  246.         offsetAmount = 0;
  247.     else {
  248.     
  249.         // Find out how much of the window lies off this screen by subtracting the amount of the window
  250.         // that is on the screen from the size of the entire window in this dimension. If the window
  251.         // is completely offscreen, the offset amount is going to be the distance from the ideal
  252.         // starting point to the first edge of the screen.
  253.         if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  254.             // See if the window is lying to the left or above the screen
  255.             if (idealEndPoint < screenEdge1)
  256.                 offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  257.             else
  258.             // Otherwise, it’s below or to the right of the screen
  259.                 offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  260.         }
  261.         else {
  262.             // Window is already partially or completely on the screen
  263.             offsetAmount = (idealEndPoint - idealStartPoint) -
  264.                             (idealOnScreenEndPoint - idealOnScreenStartPoint);
  265.     
  266.             // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  267.             if (offsetAmount != 0)
  268.                 offsetAmount += kNudgeSlop;
  269.             
  270.             // Check to see which side of the screen the window was falling off of, so that it can be
  271.             // nudged in the opposite direction.
  272.             if (idealEndPoint > screenEdge2)
  273.                 offsetAmount = -offsetAmount;
  274.         }
  275.     }
  276.     
  277.     return offsetAmount;
  278. }
  279.  
  280. /*
  281.     WindowRecord accessor functions
  282. */
  283.  
  284. RgnHandle GetWindowContentRegion(WindowPeek theWindow)
  285. {
  286.     return (theWindow->contRgn);
  287. }
  288.  
  289. RgnHandle GetWindowStructureRegion(WindowPeek theWindow)
  290. {
  291.     return (theWindow->strucRgn);
  292. }
  293.  
  294. void GetWindowPortRect(WindowPeek theWindow, Rect *portRect)
  295. {
  296.     *portRect = theWindow->port.portRect;
  297. }
  298.  
  299. void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState)
  300. {
  301.     (**((WStateDataHandle) theWindow->dataHandle)).stdState = *standardState;
  302. }
  303.  
  304. void GetWindowUserState(WindowPeek theWindow, Rect *userState)
  305. {
  306.     *userState = (**((WStateDataHandle) theWindow->dataHandle)).userState;
  307. }
  308.